home *** CD-ROM | disk | FTP | other *** search
- unit Eqnentry;
-
- interface
-
- uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Buttons,
- StdCtrls, ExtCtrls, Dialogs, SysUtils, LogicEV, Help1;
-
- type
- TEqnDlg = class(TForm)
- OKBtn: TBitBtn;
- CancelBtn: TBitBtn;
- HelpBtn: TBitBtn;
- Bevel1: TBevel;
- InputEqn: TEdit;
- procedure OKBtnClick(Sender: TObject);
- procedure CancelBtnClick(Sender: TObject);
- procedure InputEqnChange(Sender: TObject);
- procedure HelpBtnClick(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
-
- var
-
- LastEqn:string;
- EqnDlg: TEqnDlg;
-
-
-
-
- procedure DrawTruths(var TTbl:TruthType);
-
-
- implementation
-
-
- var
- thisTruth:TruthType;
-
- {$R *.DFM}
-
-
-
-
- { RmvBlanks- Removes all spaces from a string. }
-
- procedure RmvBlanks(var Equation:string);
- var dest:integer;
- i:integer;
-
- begin
-
- dest := 1;
- for i := 1 to length(Equation) do
- if (Equation[i] <> ' ') then begin
-
- Equation[dest] := Equation[i];
- dest := dest + 1;
-
- end;
-
- end;
-
-
-
-
- { DrawTruths- This procedure takes a truth table as a parameter and }
- { draws that truth table on the CREATE page. Zero variable functions }
- { produce a blank truth table, one-variable functions produce a 1x2 }
- { truth table, two-variable functions produce a 2x2 table, three- }
- { variable functions produce a 2x4 table, and four-variable functions }
- { produce a 4x4 table. }
-
- procedure DrawTruths(var TTbl:TruthType);
- var
- i,j,
- a,b,c,d,
- Clk :integer;
-
- { ToDigit converts the integer value at the given spot in the truth }
- { table to a '0' or '1' character and returns this character. }
-
- function ToDigit(clk,d,c,b,a:integer):char;
- begin
-
- result := chr(ord('0') + TTbl.tt[clk,d,c,b,a]);
-
- end;
-
- begin {DrawTruth}
-
- {Initialize all the truth table labels to spaces. We will fill }
- {in the necessary ones later. }
-
- with LogicEval do begin
-
- ba00.Caption := '';
- ba01.Caption := '';
- ba10.Caption := '';
- ba11.Caption := '';
-
- dc00.Caption := '';
- dc01.Caption := '';
- dc10.Caption := '';
- dc11.Caption := '';
-
- dc200.Caption := '';
- dc201.Caption := '';
- dc210.Caption := '';
- dc211.Caption := '';
-
- end;
-
- { Clear all the labels in the truth table. For functions with }
- { less than four variables, the unused squares need to be blank.}
- { We will fill in the non-blank ones later. }
-
- for clk := 0 to 1 do
- for a:= 0 to 1 do
- for b := 0 to 1 do
- for c := 0 to 1 do
- for d := 0 to 1 do
- begin
-
- tt[clk,d,c,b,a].Caption := '';
-
- end;
-
- { Okay, fill in the truth tables and attach appropriate labels }
- { down here. Since functions of different numbers of variables }
- { each produce completely different truth tables, the following }
- { case statement divides up the work depending on the number }
- { of variables in the function. }
-
- case TTbl.NumVars of
-
- { Handle functions of a single variable here. Produce a 1x2 }
- { truth table. }
-
- 1: begin
-
- for Clk := 0 to 1 do
- for a := 0 to 1 do
- tt[Clk,0,0,0,a].Caption := ToDigit(Clk,0,0,0,a);
-
- with LogicEval do begin
-
- ba00.Caption := ' ' + TTbl.theVars[0] + '''';
- ba01.Caption := ' ' + TTbl.theVars[0];
-
- end;
-
- end;
-
- { Handle functions of two variables here, producing a 2x2 }
- { truth table. Note that this code swaps the B and C variables }
- { in the actual truth table to get a 2x2 format rather than a }
- { 1x4 format. }
-
- 2: begin
-
- for clk := 0 to 1 do
- for a:= 0 to 1 do
- for b:= 0 to 1 do
- tt[clk,0,b,0,a].Caption := ToDigit(clk,0,0,b,a);
-
- with LogicEval do begin
-
- ba00.Caption := ' ' + TTbl.theVars[0] + '''';
- ba01.Caption := ' ' + TTbl.theVars[0];
-
- dc00.Caption := ' ' + TTbl.theVars[1] + '''';
- dc01.Caption := ' ' + TTbl.theVars[1];
-
- dc200.Caption := ' ' + TTbl.theVars[1] + '''';
- dc201.Caption := ' ' + TTbl.theVars[1];
-
- end;
-
- end;
-
-
- { Process three-variable functions down here. This code pro- }
- { duces a 2x4 truth table. }
-
- 3: begin
-
- for clk := 0 to 1 do
- for c := 0 to 1 do
- for b := 0 to 1 do
- for a := 0 to 1 do
- begin
-
- tt[clk,0,c,b,a].Caption := ToDigit(clk,0,c,b,a);
-
- end;
-
- with LogicEval do begin
-
- ba00.Caption := TTbl.theVars[1] + '''' +
- TTbl.theVars[0] + '''';
- ba01.Caption := TTbl.theVars[1] + '''' +
- TTbl.theVars[0];
- ba10.Caption := TTbl.theVars[1] +
- TTbl.theVars[0] + '''';
- ba11.Caption := TTbl.theVars[1] +
- TTbl.theVars[0];
-
- dc00.Caption := ' ' + TTbl.theVars[2] + '''';
- dc01.Caption := ' ' + TTbl.theVars[2];
-
- dc200.Caption := ' ' + TTbl.theVars[2] + '''';
- dc201.Caption := ' ' + TTbl.theVars[2];
-
- end;
-
-
- end;
-
-
- { Produce a 4x4 truth table for functions of four variables. }
-
- 4: begin
-
- for clk := 0 to 1 do
- for d := 0 to 1 do
- for c := 0 to 1 do
- for b := 0 to 1 do
- for a := 0 to 1 do
- begin
-
- tt[clk,d,c,b,a].Caption :=
- ToDigit(clk,d,c,b,a);
-
- end;
-
- with LogicEval do begin
-
- ba00.Caption := TTbl.theVars[1] + '''' +
- TTbl.theVars[0] + '''';
- ba01.Caption := TTbl.theVars[1] + '''' +
- TTbl.theVars[0];
- ba10.Caption := TTbl.theVars[1] +
- TTbl.theVars[0] + '''';
- ba11.Caption := TTbl.theVars[1] +
- TTbl.theVars[0];
-
- dc00.Caption := TTbl.theVars[3] + '''' +
- TTbl.theVars[2] + '''';
- dc01.Caption := TTbl.theVars[3] + '''' +
- TTbl.theVars[2];
- dc10.Caption := TTbl.theVars[3] +
- TTbl.theVars[2] + '''';
- dc11.Caption := TTbl.theVars[3] +
- TTbl.theVars[2];
-
- dc200.Caption := TTbl.theVars[3] + '''' +
- TTbl.theVars[2] + '''';
- dc201.Caption := TTbl.theVars[3] + '''' +
- TTbl.theVars[2];
- dc210.Caption := TTbl.theVars[3] +
- TTbl.theVars[2] + '''';
- dc211.Caption := TTbl.theVars[3] +
- TTbl.theVars[2];
-
- end;
-
-
- end;
-
- end;
-
- end;
-
-
-
- { ParseEqn- This function checks to see if an equation input by the }
- { user is syntactically correct. If not, this function }
- { returns false. If so, this function constructs the }
- { truth table for the equation by evaluating the function }
- { for all possible values of the clock, a, b, c, and d. }
-
- function ParseEqn:boolean;
- var
- a,b,c,d,
- clk,
- i :integer;
- CurChar :integer;
- Equation :string;
- TruthVars : set of char;
-
-
- { Parse- Parses the "Equation" string and evaluates it. }
- { Returns true if expression is legal, returns }
- { false if the equation is syntactically incorrect. }
- { }
- { Grammar: }
- { S -> X + S | X }
- { X -> YX | Y }
- { Y -> Y' | Z }
- { Z -> a .. z | # | ( S ) }
-
- function S:boolean;
-
- function X:boolean;
- var dummy:boolean;
-
- function Y:boolean;
-
- function Z:boolean;
- begin
-
- case (Equation[CurChar]) of
-
- { The following case handles parenthesized }
- { expressions. }
-
- '(': begin
-
- inc(CurChar); { Skip the parenthesis. }
- Result := S; { Check internal expr. }
-
- { Be sure the expression ends with a }
- { closing parenthesis. }
-
- if (Equation[CurChar] <> ')') then
- Result := false
- else inc(CurChar);
-
- end;
-
-
- { If this term is a variable name, we need }
- { check a couple of things. First of all, }
- { equations cannot have more than four dif- }
- { ferent variables. The TruthVars set con- }
- { tains the variables found in the equation }
- { up to this point. If the current vari- }
- { able is not in this set, add it to the }
- { set and bump the variable counter by one. }
- { If the variable is already in the set, }
- { then we've already counted it towards our }
- { maximum of four variables. }
- { We return true if we haven't exceeded our }
- { four variable maximum. }
-
- 'A'..'Z': begin
-
- if (not (Equation[CurChar] in TruthVars)) then
- begin
-
- thisTruth.theVars[thisTruth.NumVars]:=
- Equation[CurChar];
- TruthVars := TruthVars +
- [Equation[CurChar]];
- Result := thisTruth.NumVars < 4;
- if Result then inc(thisTruth.NumVars);
-
- end
- else Result := true;
- inc(CurChar);
-
- end;
-
-
- { The clock value ("#") and the zero and }
- { one constants do not need any special }
- { handling. Just skip over them and return }
- { true as the function result. }
-
- '#','0','1': begin
-
- Result := true;
- Inc(CurChar);
-
- end;
-
- { If not one of the above symbols, we've }
- { got an illegal symbol in the equation. }
- { Return an error if this occurs. }
-
- else Result := false;
-
- end;
- end;
-
- { Y handles all sub-expressions of the form <var>'. }
-
- begin {Y}
-
- { Note: This particular operation is left recursive }
- { and would require considerable grammar transform- }
- { ation to repair. However, a simple trick is to }
- { note that the result would have tail recursion }
- { which we can solve iteratively rather than recur- }
- { sively. Hence the while loop in the following }
- { code. }
-
- Result := Z;
- while (Result) and (Equation[CurChar] = '''') do
- inc(CurChar);
-
- end;
-
- { X handles all subexpressions containing concatenated values }
- { (i.e., logical AND operations). }
-
- begin {X}
-
- Result := Y;
- if Result and
- (Equation[CurChar] in ['A'..'Z','0','1','#','(']) then
- result := X;
-
- end;
-
-
- { S handles all general expressions and, in particular, those tak- }
- { ing the form <var> + <var>. }
-
- begin {S}
-
- Result := X;
- if Result and (Equation[CurChar] = '+') then
- begin
-
- inc(CurChar);
- Result := S;
- end;
-
- end;
-
-
-
-
- { These functions actually process an equation in order to }
- { generate the appropriate truth tables. The grammar is the }
- { same as the above, this code simply has sematic rules to }
- { actually compute results. }
-
- function E(a,b,c,d,clk:integer; Ach, Bch, Cch, Dch:char):integer;
-
- function F:integer;
-
- function G:integer;
-
- function H:integer;
- var ch:char;
- begin
-
- ch := Equation[CurChar];
- case (ch) of
-
- '(': begin
-
- inc(CurChar);
- Result := E(a,b,c,d,clk,
- Ach, Bch, Cch, Dch);
- inc(CurChar);
-
- end;
-
- 'A'..'Z': begin
-
- if (ch = Ach) then
- Result := a
- else if (ch = Bch) then
- Result := b
- else if (ch = Cch) then
- Result := c
- else if (ch = Dch) then
- Result := d;
- inc(CurChar);
-
- end;
-
- '#': begin
-
- Result := clk;
- Inc(CurChar);
-
- end;
-
-
- '0': begin
-
- Result := 0;
- Inc(CurChar);
-
- end;
-
-
- '1': begin
-
- Result := 1;
- Inc(CurChar);
-
- end;
-
- end;
- end;
-
- begin {G}
-
- Result := H;
- while (Equation[CurChar] = '''') do
- begin
-
- inc(CurChar);
- Result := Result xor 1;
-
- end;
-
- end;
-
- begin {F}
-
-
- Result := G;
- if (Equation[CurChar] in ['A'..'Z', '(', '#', '0','1']) then
- Result := Result and F; {YX case}
- end;
-
- begin {E}
-
- Result := F;
- if (Equation[CurChar] = '+') then
- begin
-
- inc(CurChar);
- Result := Result or E(a,b,c,d,clk,
- Ach, Bch, Cch, Dch);
- end;
-
- end;
-
-
-
-
-
- { Swap swaps characters in the "theVars" array. ParseEqn uses this }
- { function when it sorts the "theVars" array. }
-
- procedure swap(pos1,pos2:integer);
- var
- ch:char;
- begin
-
- ch := thisTruth.theVars[pos1];
- thisTruth.theVars[pos1] := thisTruth.theVars[pos2];
- thisTruth.theVars[pos2] := ch;
-
- end;
-
-
- begin {ParseEqn}
-
- { Note that the input equation only contains uppercase characters }
- { at this point. The code calling this function has seen to that. }
- { This statement appends a zero byte to the end of the string }
- { for use as a sentinel value. }
-
- Equation := EqnDlg.InputEqn.Text + chr(0);
-
- { Remove any spaces present in the string }
-
- RmvBlanks(Equation);
-
- { Some truth table initialization before we parse this equation: }
-
- thisTruth.NumVars := 0;
- TruthVars := [];
-
-
- { At a minimum, the equation must have four characters: "F=A" plus }
- { a zero terminating byte. If it has fewer than four characters, }
- { it cannot possibly be correct. }
-
- if (length(Equation) < 4) then
- begin
-
- MessageDlg(
- 'Syntax error, functions take the form "<var> = <expr>".',
- mtWarning, [mbok], 0);
- result := false;
- exit;
-
- end
-
- { Functions must take the form "<var> = <expr>". }
-
- else if (Equation[2] <> '=') or not (Equation[1] in ['A'..'Z', '#']) then
- begin
-
- MessageDlg(
- 'Syntax error, functions take the form "<var> = <expr>".',
- mtWarning, [mbok], 0);
- result := false;
- exit;
-
- end
-
- { Variables A..D and "#" are read-only, no functions can redefine }
- { them. Check that here. }
-
- else if (Equation[1] in ['A'..'D','#']) then
- begin
-
- MessageDlg(
- 'A-D and # are read-only and may not appear to the left of "=".',
- mtWarning, [mbok], 0);
- result := false;
- exit;
-
- end
-
- { Okay, now all that's left to check is the expression. }
-
- else begin
-
- { Set up the variable array. Fill the fifth element with the }
- { name of the function we are defining. }
-
- for i := 0 to 3 do thisTruth.theVars[i] := chr(0);
- thisTruth.theVars[4] := Equation[1];
-
- { Start just past the "<var>=" portion of the equation and }
- { check to see if this equation is syntactically correct. }
-
- CurChar := 3;
- Result := S;
-
- { If we've got too may variables, complain about that here. }
-
- if (thisTruth.NumVars > 4) then
- begin
-
- MessageDlg('Too many variables in equation (max=4).',
- mtWarning, [mbok], 0);
- result := false;
-
- end
-
- { Be sure there's no junk at the end of the equation. If we're }
- { currently pointing at the sentinel character (the zero byte) }
- { then we've processed the entire equation. If not, then there }
- { is junk at the end of the equation and we need to complain }
- { about this. }
-
- else if (Equation[CurChar] <> chr(0))then
- begin
-
- MessageDlg('Syntax Error', mtWarning, [mbok], 0);
- result := false;
-
- end;
-
-
- if not Result then exit;
-
-
- { Sort the array of characters used in this truth table. This }
- { is a simple unrolled bubble sort of four elements. }
-
- with thisTruth do begin
-
- if (NumVars >= 2) then
- begin
-
- if theVars[0] > theVars[1] then swap(0,1);
-
- if (NumVars >= 3) then
- begin
-
- if theVars[1] > theVars[2] then swap(1,2);
- if theVars[0] > theVars[1] then swap(0,1);
-
- if (NumVars = 4) then
- begin
-
- if theVars[2] > theVars[3] then swap(2,3);
- if theVars[1] > theVars[2] then swap(1,2);
- if theVars[0] > theVars[1] then swap(0,1);
-
- end;
- end;
- end;
-
-
-
- { Evaluate the function for all possible values of Clk, }
- { A, B, C, and D. Store the results away into the }
- { truth tables. }
-
- for Clk := 0 to 1 do
- for a := 0 to 1 do
- for b := 0 to 1 do
- for c := 0 to 1 do
- for d := 0 to 1 do
- begin
-
- CurChar := 3;
- tt[Clk,d,c,b,a] := e(a,b,c,d,Clk,
- theVars[0],
- theVars[1],
- theVars[2],
- theVars[3]);
- end;
-
-
- { After building the truth tables, draw them. }
-
- DrawTruths(thisTruth);
-
- end;
-
-
- end;
-
- end;
-
-
-
-
-
-
-
-
- { If the user presses the OKAY button, then we need to parse the func- }
- { tion the user has entered and, if it's correct, build the truth table }
- { for that function. }
-
- procedure TEqnDlg.OKBtnClick(Sender: TObject);
- var
- ii: integer;
- ch: char;
- begin
-
- { Convert all the characters in the equation to upper case. }
-
- InputEqn.Text := UpperCase(InputEqn.Text);
-
- { Get the name of the function we are defining. }
-
- ch := InputEqn.Text[1];
-
-
-
- with LogicEval do begin
-
- { See if this function is syntactically correct. If it is, then }
- { ParseEqn also constructs the truth table for the equation. }
-
- if (not ParseEqn) then
- begin
-
- messagebeep($ffff);
- InputEqn.Color := clRed;
-
- end
- else begin
-
- InputEqn.Color := clWhite;
-
- { EqnSet is the set of all function names we're defined up to }
- { this point. If the current function name is in this set, }
- { then the user has just entered a name of a pre-existing }
- { function. Ask the user if they want to replace the exist- }
- { ing function with the new one. }
-
- if (ch in EqnSet) then
- begin
-
- if MessageDlg('Okay to replace existing function?',
- mtWarning, [mbYes, mbNo], 0) = mrYes then
- begin
-
- { Search for the equation in the equation list. }
-
- ii := 0;
- while (EqnList.Items[ii][1] <> ch) do inc(ii);
-
- { Replace the equation and its truth table. }
-
- EqnList.Items[ii] := InputEqn.Text;
- TruthTbls[thisTruth.theVars[4]] := thisTruth;
-
- end
-
- { If the user elected not to replace the function, set }
- { thisTruth to the original truth table so we will draw }
- { the correct truth table (the original one) when we exit.}
-
- else begin
-
- thisTruth := TruthTbls[InputEqn.Text[1]];
-
- end;
-
- end
-
- { If this isn't a duplicate function definition, enter the }
- { new function into the system down here. }
-
- else begin
-
- LastEqn := InputEqn.Text;
- TruthTbls[ch] := thisTruth;
- EqnSet := EqnSet + [ch];
- EqnList.Items.add(LastEqn);
-
- end;
-
- { Draw the truth table and close the equation editor dialog box. }
-
- DrawTruths(thisTruth);
- EqnDlg.Close;
-
- end;
-
- end;
-
- end;
-
-
- { If the user presses the cancel button, close the equation editor }
- { dialog box and restore the default equation to the last equation }
- { entered in the editor (rather than the junk that is in there now). }
-
- procedure TEqnDlg.CancelBtnClick(Sender: TObject);
- begin
-
- InputEqn.Text := LastEqn;
- Close;
-
- end;
-
-
- { If there was a syntax error, the equation input box will have a red }
- { background. The moment the user changes the equation the following }
- { code will restore a white background. }
-
- procedure TEqnDlg.InputEqnChange(Sender: TObject);
- begin
-
- InputEqn.Color := clWhite;
-
- end;
-
-
-
- procedure TEqnDlg.HelpBtnClick(Sender: TObject);
- begin
- HelpBox.Show;
- end;
-
-
-
- end.
-